REPL in the first place

2|user=>
(ns intro (:require [shashurup.quf.ui :refer :all]))
nil
3|intro=>
(+ 1 2 3)
6
4|intro=>
(read-string (slurp "deps.edn"))
:mvn/repos
"jcenter"
:url"https://jcenter.bintray.com/"
}
}
:deps
org.clojure/clojure
:mvn/version"1.11.3"
}
ring/ring-core
:mvn/version"1.11.0"
}
ring/ring-jetty-adapter
:mvn/version"1.9.1"
}
ring/ring-defaults
:mvn/version"0.3.4"
}
nrepl/nrepl
:mvn/version"1.1.1"
}
compojure/compojure
:mvn/version"1.7.1"
}
}
:paths
"src""resources""target""test"
]
:aliases
:base16
:extra-deps
clj-commons/clj-yaml
:mvn/version"1.0.27"
}
}
:extra-paths
"extra/src"
]
:main-opts
"-m""shashurup.quf.base16""extra/resources/base16.edn"
]
}
:slim-bo
:main-opts
"-m""figwheel.main""-bo""slim"
]
}
:extra
:extra-deps
org.apache.tika/tika-parsers-standard-package
:mvn/version"2.9.1"
}
de.swiesend/secret-service
:mvn/version"1.8.1-jdk17"
}
org.apache.tika/tika-core
:mvn/version"2.9.1"
}
org.postgresql/postgresql
:mvn/version"42.7.1"
}
org.clojure/data.csv
:mvn/version"1.0.1"
}
org.clojure/java.jdbc
:mvn/version"0.7.12"
}
clj-commons/clj-yaml
:mvn/version"1.0.27"
}
org.jetbrains.pty4j/pty4j
:mvn/version"0.12.25"
}
cheshire/cheshire
:mvn/version"5.12.0"
}
com.novemberain/monger
:mvn/version"3.6.0"
}
}
:extra-paths
"extra/src""extra/resources"
]
}
:fat
:main-opts
"-m""figwheel.main""-b""fat"
]
}
:fig
:main-opts
"-m""figwheel.main"
]
}
:build
:deps
io.github.clojure/tools.build
:mvn/version"0.10.1"
}
}
:ns-defaultbuild
}
:slim
:main-opts
"-m""figwheel.main""-b""slim"
]
}
:fat-bo
:main-opts
"-m""figwheel.main""-bo""fat"
]
}
:cljs
:extra-deps
org.clojure/clojurescript
:mvn/version"1.11.132"
}
com.bhauman/figwheel-main
:mvn/version"0.2.18"
}
crate/crate
:mvn/version"0.2.5"
}
cljsjs/openlayers
:mvn/version"7.5.2-0"
}
cljsjs/chartjs
:mvn/version"3.9.1-0"
}
}
}
:dev
:main-opts
"-m""figwheel.main""-b""dev""-r"
]
}
}
}

By now, there is nothing special here. Of course you may play with it and figure that parts of the datastructure can be collapsed. However that is not what I dreamt about. One of the ideas I wanted to play with was having structured data in shell piplines. Here is where the ideas cross - lisp is a natural choice to represent such kind of data. So I started with filesystem module.

A shell, a bit improved

7|intro=>
(require '[shashurup.quf.fs :as f])
nil
8|intro=>
(f/c "~/devel/quasi-una-fantasia")
"/home/georgy/devel/quasi-una-fantasia"
9|intro=>
(f/l :m "resources/public")
SizeModifiedName
4KMay 5 12:01css
602Apr 30 17:18fat.html
616Apr 30 17:03index.html
533Apr 30 17:04slim.html
151Apr 25 2022test.html

Hey, what has just happened? This doesn't look like a clojure data structure!

11|intro=>
(with-meta (f/l :m "resources/public") {})
:path"/home/georgy/devel/quasi-una-fantasia/resources/public/css"
:permissions
:owner-write:others-read:others-execute:group-read:owner-read:owner-execute:group-execute
}
:group"georgy"
:mime-typenil
:name"css"
:created1714899699163
:modified1714899699163
:accessed3429742642453
:size4096
:symlink?false
:regular-file?false
:directory?true
:user"georgy"
}
:path"/home/georgy/devel/quasi-una-fantasia/resources/public/fat.html"
:permissions
:owner-write:others-read:group-read:owner-read
}
:group"georgy"
:mime-type"text/html"
:name"fat.html"
:created1714486722332
:modified1714486722332
:accessed1715181181329
:size602
:symlink?false
:regular-file?true
:directory?false
:user"georgy"
}
:path"/home/georgy/devel/quasi-una-fantasia/resources/public/index.html"
:permissions
:owner-write:others-read:group-read:owner-read
}
:group"georgy"
:mime-type"text/html"
:name"index.html"
:created1714485787992
:modified1714485787992
:accessed1715180575551
:size616
:symlink?false
:regular-file?true
:directory?false
:user"georgy"
}
:path"/home/georgy/devel/quasi-una-fantasia/resources/public/slim.html"
:permissions
:owner-write:others-read:group-read:owner-read
}
:group"georgy"
:mime-type"text/html"
:name"slim.html"
:created1714485856331
:modified1714485856331
:accessed1715181181329
:size533
:symlink?false
:regular-file?true
:directory?false
:user"georgy"
}
:path"/home/georgy/devel/quasi-una-fantasia/resources/public/test.html"
:permissions
:owner-write:others-read:group-read:owner-read
}
:group"georgy"
:mime-type"text/html"
:name"test.html"
:created1650873062000
:modified1650873062000
:accessed1715181181329
:size151
:symlink?false
:regular-file?true
:directory?false
:user"georgy"
}
)

Here they are - our clojure data structures.

13|intro=>
(meta (f/l :m "resources/public"))
:shashurup.quf/hint
:table:shashurup.quf.fs/file
:size:modified:name-ex
]
]
}
14|intro=>
(with-meta [:p "The magic is caused by the special metadata attached to our clojure data representing these files. " "Metadata activates custom client side code which may render data returned by a function in a special way. " "In this case we have specialized renderer for files. (See " [:a {:href "fs.cljs"} "fs.cljs"] " module for example). " "There are also some generic renderers such as " [:span.quf-keyword ":table"] " or " [:span.quf-keyword ":html"] ", check it out."] {:shashurup.quf/hint :html})

The magic is caused by the special metadata attached to our clojure data representing these files. Metadata activates custom client side code which may render data returned by a function in a special way. In this case we have specialized renderer for files. (See fs.cljs module for example). There are also some generic renderers such as :table or :html, check it out.

15|intro=>
(table [{:name "Giuseppe" :age 23} {:name "Василий" :age 42} {:name "Ὅμηρος" :age :undefined}])
NameAge
Giuseppe23
Василий42
Ὅμηρος:undefined

There are convenience functions such as table which add appropriate metadata

17|intro=>
(f/l :m "*.edn")
SizeModifiedName
2.4KMay 1 09:08deps.edn
521Apr 29 14:03dev.cljs.edn
583May 1 07:30fat.cljs.edn
1.2KFeb 17 08:46figwheel-main.edn
121Apr 30 17:04slim.cljs.edn
460Jun 22 17:41test.cljs.edn
18|intro=>
(f/f "*.clj")
Name
doc/doc.clj
extra/src/shashurup/quf/theme.clj
extra/src/shashurup/quf/data.clj
extra/src/shashurup/quf/fs.clj
extra/src/shashurup/quf/sh.clj
extra/src/shashurup/quf/base16.clj
extra/src/shashurup/quf/geo.clj
extra/src/shashurup/quf/chart.clj
extra/src/shashurup/quf/secrets.clj
extra/src/shashurup/quf/db.clj
src/shashurup/quf/ui.clj
src/shashurup/quf/vars.clj
src/shashurup/quf/response.clj
src/shashurup/quf/srv.clj
build.clj
target/slim-jar/shashurup/quf/vars.clj
target/slim-jar/shashurup/quf/response.clj
target/slim-jar/shashurup/quf/srv.clj
19|intro=>
(f/l :m)
SizeModifiedName
4KFeb 4 09:10base16
4KMay 9 10:58doc
4KApr 29 13:23extra
4KApr 29 13:27resources
4KJun 22 17:35src
4KMay 1 09:35target
4KJun 22 17:36test
2.4KMay 1 09:37build.clj
2.4KMay 1 09:08deps.edn
521Apr 29 14:03dev.cljs.edn
1.1KMay 9 12:38dummy-auto-testing.js
583May 1 07:30fat.cljs.edn
1.2KFeb 17 08:46figwheel-main.edn
171Apr 29 13:30install-xterm.js.sh
25KMar 24 18:18package-lock.json
89Mar 23 17:53package.json
5.1KMay 5 17:03project.org
2.8KMay 1 09:15README.md
121Apr 30 17:04slim.cljs.edn
460Jun 22 17:41test.cljs.edn
476Jan 8 10:15test.py
20|intro=>
$$s
"/home/georgy/devel/quasi-una-fantasia/package-lock.json""/home/georgy/devel/quasi-una-fantasia/package.json""/home/georgy/devel/quasi-una-fantasia/test.py"
]

$$s is so called client side variable. It is evaluated before an expression is sent to backend for evaluation. $$s is for the nearest cell with checkboxes (they appear when you press Ctrl+m), $$s-all is for all checkboxes on the page etc.These variables are controlled by client side code and cannot be assigned.

22|intro=>
(f/r "package.json")
"dependencies"
"@cljs-oss/module-deps""^1.1.1"
"xterm""^5.1.0"
}
}

This detects file type and parses it accordingly. XML and CSV are also supported.

24|intro=>
(f/c "~/wallpapers")
"/home/georgy/wallpapers"
25|intro=>
(f/l :c "autumn*.jpg")
autumn-trees-painting-wallpaper-1.jpg
autumn_forest_4k_2-2560x1440.jpg
autumn_park_scenery_5k-2560x1440.jpg

:c makes output to appear as a list of thumbnails. (Ctrl+m also works here)

27|intro=>
(f/v "20230220_154530.jpg")

Conventional shell

29|intro=>
(require '[shashurup.quf.sh :refer [! !>]])
nil
30|intro=>
(f/c "~/devel/quasi-una-fantasia")
"/home/georgy/devel/quasi-una-fantasia"
31|intro=>
(!> "cat dev.cljs.edn")
^{:watch-dirs ["test" "src" "extra/src"]
  :css-dirs ["resources/public/css"
             "extra/resources/public/css"]
  :auto-testing true}
{:modules {:core {:entries #{shashurup.quf.core}}
           :fs {:entries #{shashurup.quf.fs}}
           :chartjs {:entries #{shashurup.quf.chartjs}}
           :ol {:entries #{shashurup.quf.ol}}
           :terminal {:entries #{shashurup.quf.terminal}}
           :theme {:entries #{shashurup.quf.theme}}
           }
 :output-to "dummy.js"
 :externs ["xtermjs-externs.js"]
}
32|intro=>
(require '[clojure.string :as s])
nil
33|intro=>
(update-vals (->> (!> "pacman -Qg") (map #(s/split % #" ")) (group-by first)) #(map second %))
"qt6"
"qt6-5compat""qt6-base""qt6-declarative""qt6-imageformats""qt6-multimedia""qt6-shadertools""qt6-svg""qt6-translations""qt6-wayland"
)
"xorg-fonts"
"xorg-fonts-encodings"
)
"xorg-apps"
"xorg-iceauth""xorg-mkfontscale""xorg-setxkbmap""xorg-xauth""xorg-xbacklight""xorg-xdpyinfo""xorg-xev""xorg-xhost""xorg-xinput""xorg-xkbcomp""xorg-xmodmap""xorg-xprop""xorg-xrandr""xorg-xrdb""xorg-xset""xorg-xsetroot""xorg-xwininfo"
)
"gnome"
"evince""gnome-keyring""gvfs""simple-scan"
)
"pyqt5"
"python-pyqt5"
)
"gnome-extra"
"seahorse"
)
"pantheon"
"elementary-icon-theme"
)
"i3"
"i3-wm""i3status"
)
"python-build-backend"
"python-setuptools"
)
"xorg-drivers"
"xf86-input-libinput""xf86-video-amdgpu"
)
"tree-sitter-grammars"
"tree-sitter-bash""tree-sitter-c""tree-sitter-lua""tree-sitter-markdown""tree-sitter-python""tree-sitter-query""tree-sitter-vimdoc"
)
"kf6"
"kcoreaddons"
)
"pro-audio"
"fluidsynth"
)
"xfce4-goodies"
"xfce4-notifyd"
)
"vulkan-devel"
"spirv-tools""vulkan-headers"
)
"xfce4"
"exo""garcon""thunar""thunar-volman""tumbler""xfce4-appfinder""xfce4-panel""xfce4-power-manager""xfce4-session""xfce4-settings""xfce4-terminal""xfconf""xfdesktop""xfwm4""xfwm4-themes"
)
"qt5"
"qt5-3d""qt5-base""qt5-declarative""qt5-doc""qt5-location""qt5-multimedia""qt5-quickcontrols""qt5-remoteobjects""qt5-serialport""qt5-svg""qt5-tools""qt5-translations""qt5-webchannel""qt5-webengine""qt5-websockets""qt5-x11extras"
)
"xorg"
"xorg-fonts-encodings""xorg-iceauth""xorg-mkfontscale""xorg-server""xorg-server-common""xorg-server-xephyr""xorg-server-xnest""xorg-setxkbmap""xorg-xauth""xorg-xbacklight""xorg-xdpyinfo""xorg-xev""xorg-xhost""xorg-xinput""xorg-xkbcomp""xorg-xmodmap""xorg-xprop""xorg-xrandr""xorg-xrdb""xorg-xset""xorg-xsetroot""xorg-xwininfo"
)
}
34|intro=>
(! "ls --color /etc")
0

Escape sequences are handled via xterm.js. And, yes, you can run Vim here.

Databases

It was a bit hard to resist temptation to plug jdbc infrastructure

38|intro=>
(require '[shashurup.quf.db :as db])

WARNING: random-uuid already refers to: #'clojure.core/random-uuid in namespace: monger.util, being replaced by: #'monger.util/random-uuid

nil
39|intro=>
(db/c "postgresql://postgres:123@localhost/postgres")
varshashurup.quf.db/*current*
)
40|intro=>
(db/q "select id, name, country_id from capitals limit 10")
idnamecountry_id
1Sukhumi68
2Kabul31
3Mariehamn142
4Tirana4
5Algiers2
7Andorra la Vella188
8Luanda6
9The Valley128
11St. Johns1
12Buenos Aires8
41|intro=>
(db/q "select id, name from countries where id < ?" 8)
idname
1Antigua and Barbuda
2Algeria
3Azerbaijan
4Albania
5Armenia
6Angola
7American Samoa
42|intro=>
(html [:h3 "Geo"])

Geo

43|intro=>
(require '[shashurup.quf.geo :as g])
nil
44|intro=>
(db/q "select id, name from countries where name like 'W%'")
idname
219Wallis and Futuna Islands
237Western Sahara
45|intro=>
(g/v (db/q "select geom, name, id from countries where name like 'Po%'"))

Charts

47|intro=>
(require '[shashurup.quf.chart :as chart])
nil
48|intro=>
(chart/histogram (repeatedly 100 #(rand-int 100)))

Dessert

50|intro=>
(require '[shashurup.quf.theme :as theme])
nil
51|intro=>
(theme/ls "gruv")
(defn function1 [data] {:name "Just a string" :value 42 :valid false})
Gruvbox light, hard
(defn function1 [data] {:name "Just a string" :value 42 :valid false})
Gruvbox dark, medium
(defn function1 [data] {:name "Just a string" :value 42 :valid false})
Gruvbox dark, soft
(defn function1 [data] {:name "Just a string" :value 42 :valid false})
Gruvbox light, medium
(defn function1 [data] {:name "Just a string" :value 42 :valid false})
Gruvbox dark, hard
(defn function1 [data] {:name "Just a string" :value 42 :valid false})
Gruvbox dark, pale
(defn function1 [data] {:name "Just a string" :value 42 :valid false})
Gruvbox light, soft

There is a collection of base16 themes, use ls to browse, click to apply, enjoy!

53|intro=>